package com.xiye.im.config;

import com.xiye.common.session.redis.RedisSessionDAO;
import com.xiye.common.session.redis.impl.MyWebSessionManager;
import com.xiye.common.session.redis.impl.RedisCacheManager;
import com.xiye.common.session.redis.impl.RedisManager;
import com.xiye.common.session.shiro.ShiroRealm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author 西野
 * @version V1.0
 * @title ShiroConfiguration.java
 * @package com.xiye.zuul.config
 * @description shiro配置文件
 * @date 2018-06-15
 */
@Configuration
public class ShiroConfiguration {
    @SuppressWarnings("unused")
    private static final Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);

    /*
     * @Author 西野
     * @Date 2018-06-15 16:06:06
     * @Description 注册DelegatingFilterProxy（Shiro）,等同于传统项目中web.xml中配置filter
     * @Param []
     * @Return org.springframework.boot.web.servlet.FilterRegistrationBean
     */
    @Bean(name = "filterRegistrationBean")
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
        // 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
        filterRegistration.addInitParameter("targetFilterLifecycle", "true");
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns("/*");
        return filterRegistration;
    }

    /*
     * @Author 西野
     * @Date 2018-06-15 15:06:06
     * @Description Shiro's main business-tier object for web-enabled applications；Shiro安全管理器
     * @Param []
     * @Return org.apache.shiro.web.mgt.DefaultWebSecurityManager
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setSessionManager(defaultWebSessionManager());
        securityManager.setRealm(almShiroRealm());
        securityManager.setCacheManager(redisCacheManager());
        // <!-- 用户授权/认证信息Cache, 采用EhCache 缓存 -->
        /*dwsm.setCacheManager(getEhCacheManager());*/
        return securityManager;
    }

    /*
     * @Author 西野
     * @Date 2018-06-15 15:06:06
     * @Description 会话管理器
     * @Param []
     * @Return com.xiye.common.session.redis.impl.MyWebSessionManager
     */
    @Bean(name = "defaultWebSessionManager")
    public MyWebSessionManager defaultWebSessionManager() {
        MyWebSessionManager defaultWebSessionManager = new MyWebSessionManager();
        /*相隔多久检查一次session的有效性（毫秒单位）*/
        defaultWebSessionManager.setSessionValidationInterval(259200000);
        /* session 有效时间（毫秒单位）
         * 通过查询shiro中session的api得知（SecurityUtils.getSubject().getSession().getTimeout()接口中timeout类型为Long）
         * 2141483647为Long最大值，约24天
         */
        defaultWebSessionManager.setGlobalSessionTimeout(2141483647);
        /*是否开启会话验证器任务 默认true ，此处不起作用，com.xiye.session.redis.impl.MyWebSessionManager重写时在该类中写死为true*/
        defaultWebSessionManager.setSessionValidationSchedulerEnabled(false);
        /*是否在会话过期后会调用SessionDAO的delete方法删除会话 默认true*/
        defaultWebSessionManager.setDeleteInvalidSessions(true);
        defaultWebSessionManager.setSessionDAO(redisShiroSessionDAO());
        defaultWebSessionManager.setSessionIdCookie(sessionIdCookie());
        return defaultWebSessionManager;
    }

    /*
     * @Author 西野
     * @Date 2018-06-15 15:06:06
     * @Description 会话Cookie模板 sessionIdCookie：maxAge=-1表示浏览器关闭时失效此Cookie
     * @Param []
     * @Return org.apache.shiro.web.servlet.SimpleCookie
     */
    @Bean(name = "sessionIdCookie")
    public SimpleCookie sessionIdCookie() {
        SimpleCookie sessionIdCookie = new SimpleCookie();
        /* 指定本系统SESSIONID, 默认为: JSESSIONID 问题: 与SERVLET容器名冲突, 如JETTY, TOMCAT
         * 等默认JSESSIONID, 当跳出SHIRO SERVLET时如ERROR-PAGE容器会为JSESSIONID重新分配值导致登录会话丢失!
         */
        sessionIdCookie.setName("xiye.session.id");
        sessionIdCookie.setHttpOnly(true);
        /*设置cookie保存时间，单位毫秒 1000*60*60*24*30=259200000（30天），防止已登录过的用户关闭浏览器再打开时需再次登录*/
        sessionIdCookie.setMaxAge(259200000);
        return sessionIdCookie;
    }

    /*
     * @Author 西野
     * @Date 2018-06-15 15:06:06
     * @Description 项目自定义的Realm
     * @Param []
     * @Return com.xiye.common.session.shiro.ShiroRealm
     */
    @Bean(name = "almShiroRealm")
    public ShiroRealm almShiroRealm() {
        ShiroRealm almShiroRealm = new ShiroRealm();
        return almShiroRealm;
    }

    /*
     * @Author 西野
     * @Date 2018-06-15 15:06:06
     * @Description Shiro主过滤器本身功能十分强大，其强大之处就在于它支持任何基于URL路径表达式的、
     * 自定义的过滤器的执行 Web应用中，Shiro可控制的Web请求必须经过Shiro主过滤器的拦截，
     * Shiro对基于Spring的Web应用提供了完美的支持
     * @Param []
     * @Return org.apache.shiro.spring.web.ShiroFilterFactoryBean
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter() {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        /*shiro的核心安全接口，这个属性是必须的*/
        shiroFilter.setSecurityManager(securityManager());
        /*要求登录时的链接(登录页面地址)，非必须的属性，默认会自动寻找Web工程根目录下的"/login.jsp"页面*/
        //shiroFilter.setLoginUrl("/");
        /*登录成功后要跳转的连接*/
        //shiroFilter.setSuccessUrl("/main/index");
        /*用户访问未对其授权的资源时，所显示的连接*/
        //shiroFilter.setUnauthorizedUrl("/doc.html");
        /*
         * anon：它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main这种
         * authc：该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
         */
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        /*子项目的请求不拦截，正常放行，权限验证在AccessUserFilter拦截器中进行处理*/

        filterChainDefinitionMap.put("/**/login**/**", "anon");

        /* /**authc只能放在最下面，否则会报错 */
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilter;
    }

    @Bean(name = "redisShiroSessionDAO")
    public RedisSessionDAO redisShiroSessionDAO() {
        RedisSessionDAO redisShiroSessionDAO = new RedisSessionDAO();
        redisShiroSessionDAO.setRedisManager(redisManager());
        return redisShiroSessionDAO;
    }

    @Bean(name = "redisCacheManager")
    public RedisCacheManager redisCacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    @Bean(name = "redisManager")
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        return redisManager;
    }

    /*
     * @Author 西野
     * @Date 2018-06-15 16:06:06
     * @Description Shiro生命周期处理器 cipherKey是加密rememberMe Cookie的密钥；
     * 默认AES算法；保证实现了Shiro内部lifecycle函数的bean执行
     * @Param []
     * @Return org.apache.shiro.spring.LifecycleBeanPostProcessor
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        LifecycleBeanPostProcessor lifecycleBeanPostProcessor = new LifecycleBeanPostProcessor();
        return lifecycleBeanPostProcessor;
    }

}
